package miniMax;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.Vector;

import domain.Agent;
import domain.AgentSmart;
import domain.Board;
import domain.Flag;
import domain.Game.action;

public class MiniMax {

	public Agent 						_agentMax;
	public Agent		 				_agentMin;
	//private PriorityQueue<MiniMaxNode> 	_priorityQ;
	private int 						_maxDepth;
	
	public MiniMax(Agent agentMax, Agent agentMin) {
		this._agentMax = agentMax;
		this._agentMin = agentMin;
        //this._priorityQ = new PriorityQueue<MiniMaxNode>();
        this._maxDepth 	= 3;
	}
	
	public LinkedList<action> runMiniMaxSearch(Board board, Vector<Agent> agents, Vector<Flag> flags) {
		
		LinkedList<action> ans = new LinkedList<action>();
		int alpha = Integer.MIN_VALUE;		// -Infinity
		int beta  = Integer.MAX_VALUE;		// +Infinity
		
		MiniMaxNode root = new MiniMaxNode(board, agents, flags, ans, this._agentMax, this._agentMin);
		// root now has clones of board, agents & flags.
		
		//this._priorityQ.add(root);
		
		AlphaBetaPruningResult res = alphaBeta(root.getAgentMax(), root, alpha, beta, this._maxDepth);
		
		ans.addLast(res.get_act());
		
		return ans;
	}

	private AlphaBetaPruningResult alphaBeta(Agent agentInTurn, MiniMaxNode node, int alpha, int beta, int counter) {
		if (endCondition(node, counter)){
			return new AlphaBetaPruningResult(node.getValue(), node.getActionThatGotMeHere());
		}
		else{
			counter--;
			
        	//Extract:
            MiniMaxNode after_up      = node.simulateNode(agentInTurn, action.UP);
            MiniMaxNode after_right   = node.simulateNode(agentInTurn, action.RIGHT);
            MiniMaxNode after_down    = node.simulateNode(agentInTurn, action.DOWN);
            MiniMaxNode after_left    = node.simulateNode(agentInTurn, action.LEFT);
            MiniMaxNode after_shoot   = node.simulateNode(agentInTurn, action.SHOOT);
            
            System.out.println(after_up + "\n" + after_right + "\n" + after_down + "\n" + after_left + "\n" + after_shoot + "\n");
            
            Vector<MiniMaxNode> children = new Vector<MiniMaxNode>();
            children.addElement(after_up);
            children.addElement(after_right);
            children.addElement(after_down);
            children.addElement(after_left);
            children.addElement(after_shoot);
            
            MiniMaxNode chosenNode = after_up;
            if (isMaxsTurn(agentInTurn)){
            	for (Iterator iter = children.iterator(); iter.hasNext();) {
					MiniMaxNode child = (MiniMaxNode) iter.next();
					Agent min = child.getAgentMin();
					AlphaBetaPruningResult res = alphaBeta(min, child, alpha, beta, counter); 
					int score = res.get_value();
		            if (score > alpha){
		            	alpha 		= score;		// we have found a better best move.
		            	chosenNode	= child;		// save the node that this move causes you to reach.
		            }
		            ///*
		            if (alpha >= beta){
		            	AlphaBetaPruningResult tmp = new AlphaBetaPruningResult(alpha, child.getActionThatGotMeHere());	// cut off
		            	System.out.println("CUT OFF\nResult is: " + tmp);
		            	return tmp;
		            }
		            //*/
		            //printChildren(children);
				}
            	AlphaBetaPruningResult res = new AlphaBetaPruningResult(alpha, chosenNode.getActionThatGotMeHere());	// this is our best move
            	System.out.println("Result is: " + res);
            	return res;
			}
			else if (isMinsTurn(agentInTurn)){
            	for (Iterator iter = children.iterator(); iter.hasNext();) {
					MiniMaxNode child = (MiniMaxNode) iter.next();
					Agent max = child.getAgentMax();
					AlphaBetaPruningResult res = alphaBeta(max, child, alpha, beta, counter); 
					int score = res.get_value();
		            if (score < beta){
		            	beta		= score;		// opponent has found a better worse move
		            	chosenNode	= child;		// save the node that this move causes you to reach.
		            }
		            ///*
		            if (alpha >= beta){
		            	AlphaBetaPruningResult tmp = new AlphaBetaPruningResult(beta, child.getActionThatGotMeHere());		// cut off
		            	System.out.println("CUT OFF\nResult is: " + tmp);
		            	return tmp;
		            }
		            //*/
				}
            	AlphaBetaPruningResult res = new AlphaBetaPruningResult(beta, chosenNode.getActionThatGotMeHere());				// this is the opponent's best move
            	System.out.println("Result is: " + res);
            	return res;
			}
			else{	// No one's turn?!?
				System.err.println(getClass().getName()+": Not supposed to get here...");
				return null;
			}
		}
	}

	private void printChildren(Vector<MiniMaxNode> children) {
		System.out.print("Children:  ");
		for (Iterator child = children.iterator(); child.hasNext();) {
			MiniMaxNode mmn = (MiniMaxNode) child.next();
			System.out.print(mmn.getValue() + "  ");
		}
		System.out.println();
	}

	private boolean endCondition(MiniMaxNode node, int counter) {
		System.out.println("counter: "+ counter);	// del
		return node.isGoalNode() || counter == 0;
	}

	private boolean isMaxsTurn(Agent agent) {
		//return agent == this._agentMax;
		return agent instanceof AgentSmart;
	}

	private boolean isMinsTurn(Agent agent) {
		return !(agent instanceof AgentSmart);
	}
	
//	alpha-beta(player,board,alpha,beta)
//    if(game over in current board position)
//        return winner
//
//    children = all legal moves for player from this board
//    if(max's turn)
//        for each child
//            score = alpha-beta(other player,child,alpha,beta)
//            if score > alpha then alpha = score (we have found a better best move)
//            if alpha >= beta then return alpha (cut off)
//        return alpha (this is our best move)
//    else (min's turn)
//        for each child
//            score = alpha-beta(other player,child,alpha,beta)
//            if score < beta then beta = score (opponent has found a better worse move)
//            if alpha >= beta then return beta (cut off)
//        return beta (this is the opponent's best move)

	
	
}
